package loginlogic

import (
	"context"

	"go-zero-admin/apps/system/cmd/rpc/internal/logic/captcha"
	"go-zero-admin/apps/system/cmd/rpc/internal/logic/loginlog"
	"go-zero-admin/apps/system/cmd/rpc/internal/logic/user"
	"go-zero-admin/apps/system/cmd/rpc/internal/svc"
	"go-zero-admin/apps/system/cmd/rpc/syspb"
	"go-zero-admin/pkg/constant"
	"go-zero-admin/pkg/tool"
	"go-zero-admin/pkg/xerr"

	"github.com/duke-git/lancet/v2/convertor"
	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/mr"
	"github.com/zeromicro/go-zero/core/service"
)

type LoginLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
	captchaForVerifyLogic *captchalogic.CaptchaForVerifyLogic
	getUserByAccountLogic *userlogic.GetUserByAccountLogic
	upUserLoginLogic      *userlogic.UpUserLoginLogic
	generateTokenLogic    *GenerateTokenLogic
	getUserRulesLogic     *userlogic.GetUserRulesLogic
	addLoginLogLogic      *loginloglogic.AddLoginLogLogic
}

func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
	return &LoginLogic{
		ctx:                   ctx,
		svcCtx:                svcCtx,
		Logger:                logx.WithContext(ctx),
		captchaForVerifyLogic: captchalogic.NewCaptchaForVerifyLogic(ctx, svcCtx),
		getUserByAccountLogic: userlogic.NewGetUserByAccountLogic(ctx, svcCtx),
		upUserLoginLogic:      userlogic.NewUpUserLoginLogic(ctx, svcCtx),
		generateTokenLogic:    NewGenerateTokenLogic(ctx, svcCtx),
		getUserRulesLogic:     userlogic.NewGetUserRulesLogic(ctx, svcCtx),
		addLoginLogLogic:      loginloglogic.NewAddLoginLogLogic(ctx, svcCtx),
	}
}

func (l *LoginLogic) Login(in *syspb.LoginReq) (*syspb.LoginResp, error) {
	if in.GetVerifyKey() == "" || in.GetVerifyCode() == "" || in.GetUserName() == "" || in.GetPassword() == "" ||
		in.GetIp() == "" || in.GetUserAgent() == "" {
		return nil, errors.Wrapf(xerr.NewErrMsg("用户登陆失败：参数缺失"),
			tool.GetErrMsgFormat("user login"), "params is not existed", in)
	}

	var (
		user        *syspb.SysUser
		token       string
		permissions []string
		tree        []*syspb.SysMenuTree
	)

	// 如果不是开发模式，则判断验证码是否正确
	mode := l.svcCtx.Config.Mode
	if mode != service.DevMode {
		verifyResp, err := l.captchaForVerifyLogic.CaptchaForVerify(&syspb.CaptchaForVerifyReq{
			Id:     in.GetVerifyKey(),
			Answer: in.GetVerifyCode(),
		})
		if err != nil {
			return nil, errors.Wrapf(xerr.NewErrMsg("用户登陆失败：验证码错误"),
				tool.GetErrMsgFormat("user login"), "get captcha error", in)
		}
		isTrue := verifyResp.IsTrue
		if !isTrue {
			return nil, errors.Wrapf(xerr.NewErrMsg("用户登陆失败：验证码错误"),
				tool.GetErrMsgFormat("user login"), "get captcha error", in)
		}
	}

	// 根据账户密码获取用户信息
	userResp, err := l.getUserByAccountLogic.GetUserByAccount(&syspb.GetUserByAccountReq{
		UserName: in.GetUserName(),
		Password: in.GetPassword(),
	})
	if err != nil {
		// 保存登录失败的日志信息
		go func() {
			_, _ = l.addLoginLogLogic.AddLoginLog(&syspb.AddLoginLogReq{
				UserName:  in.GetUserName(),
				Ip:        in.GetIp(),
				UserAgent: in.GetUserAgent(),
				Msg:       err.Error(),
				Status:    constant.LoginLogStatusFail,
				Module:    constant.LoginLogModuleSys,
			})
		}()
		return nil, err
	}
	user = userResp.GetOne()
	userId := user.GetId()

	// 更新用户登录消息
	_, err = l.upUserLoginLogic.UpUserLogin(&syspb.UpUserLoginReq{
		Id:         userId,
		Ip:         in.GetIp(),
		UpdateUser: convertor.ToString(userId),
	})
	if err != nil {
		return nil, err
	}

	// 报存登录成功的日志信息
	go func() {
		_, _ = l.addLoginLogLogic.AddLoginLog(&syspb.AddLoginLogReq{
			Status:     constant.LoginLogStatusSuc,
			UserName:   in.GetUserName(),
			Ip:         in.GetIp(),
			UserAgent:  in.GetUserAgent(),
			Msg:        constant.LoginLogMsg,
			Module:     constant.LoginLogModuleSys,
			CreateUser: convertor.ToString(userId),
			UpdateUser: convertor.ToString(userId),
		})
	}()

	// 获取token和用户权限数据
	if err = mr.Finish(func() (err error) {
		tokenResp, err := l.generateTokenLogic.GenerateToken(&syspb.GenerateTokenReq{UserId: userId})
		if err != nil {
			return err
		}
		token = tokenResp.GetAccessToken()
		return
	}, func() (err error) {
		rulesResp, err := l.getUserRulesLogic.GetUserRules(&syspb.GetUserRulesReq{Id: userId})
		if err != nil {
			return err
		}
		tree = rulesResp.GetTree()
		permissions = rulesResp.GetPermissions()
		return
	}); err != nil {
		return nil, err
	}

	return &syspb.LoginResp{
		User:        user,
		Token:       token,
		Tree:        tree,
		Permissions: permissions,
	}, nil
}
